﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using AtletIS.DomainModel.Entities.Main;
using AtletIS.Repository.UnitOfWork;
using AtletIS.Service.Enums;
using AtletIS.Service.Base;

namespace AtletIS.Service.Services
{
    public class PerformancesService : ServiceBase
    {

        #region Constructor/Dispose

        public PerformancesService()
        {
            this._unitOfWork = new UnitOfWork();
        }

        public PerformancesService(IUnitOfWork unitOfWork)
        {
            this._unitOfWork = unitOfWork;
        }

        #endregion

        /// <summary>
        ///  Načíta pretekárov podľa ID disciplíny
        /// </summary>
        public List<Performance> GetByDisciplineListId(int disciplineListId, PerformancesSortEnum sort, bool includeExtTexts = false, bool includeChipSplits = false)
        {
            var query = this._unitOfWork.Repository<Performance>()
                    .Query(p => p.DisciplineListId == disciplineListId)
                    .Include(p => p.Competitor)
                    .Include(p => p.Country)
                    .Include(p => p.ResultCode);

            if (includeExtTexts)
            {
                query = query.Include(p => p.ExtTexts);
            }

            if (includeChipSplits)
            { 
                // TODO: Chip splits
            }

            List<Performance> performances = query.Select().ToList();
            performances = this.Sort(performances, sort);
            return performances;
        }

        /// <summary>
        ///  Načíta pretekárov podľa ID disciplíny
        /// </summary>
        public List<Performance> GetByDisciplineListId(int disciplineListId, PerformancesSortEnum sort, out int totalCount, bool includeExtTexts = false, bool includeChipSplits = false, int? page = null, int? pageSize = null)
        {
            List<Performance> performances = GetByDisciplineListId(disciplineListId, sort, includeExtTexts, includeChipSplits);
            
            totalCount = performances.Count;

            if (page.HasValue && pageSize.HasValue)
            {
                performances = performances.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value).ToList();
            }

            return performances;
        }

        /// <summary>
        ///  Načíta všetkých pretekárov v disciplínach rozdelenej disciplíny
        /// </summary>
        public List<Performance> GetByParentDisciplineListId(int disciplineListId, PerformancesSortEnum sort)
        {
            List<Performance> performances = new List<Performance>();
            List<DisciplineList> subDisciplineList = new DisciplineListService(this._unitOfWork).GetByParentId(disciplineListId);

            // Take ALL from each heat
            foreach (DisciplineList dl in subDisciplineList)
            { 
                performances.AddRange(this.GetByDisciplineListId(dl.ID, sort));
            }

            performances = this.Sort(performances, sort);
            return performances;
        }

        /// <summary>
        ///  Načíta prvých N pretekárov v disciplínach rozdelenej disciplíny, doplní najlepšími v celkovom poradí
        /// </summary>
        internal List<Performance> GetByParentDisciplineListIdBestInHeatThenBestInTotal(int disciplineListId, int takeFromHeat, int total, PerformancesSortEnum sort)
        {
            List<Performance> advPerformances = new List<Performance>();
            List<Performance> othPerformances = new List<Performance>();

            List<DisciplineList> subDisciplineList = new DisciplineListService(this._unitOfWork).GetByParentId(disciplineListId);

            // Take TopN from each heat
            foreach (DisciplineList dl in subDisciplineList)
            {
                List<Performance> perfs = this.GetByDisciplineListId(dl.ID, sort);

                advPerformances.AddRange(perfs.Take(takeFromHeat));
                othPerformances.AddRange(perfs.Skip(takeFromHeat));
            }

            // Add from other to reach total
            if (advPerformances.Count < total)
            {
                othPerformances = this.Sort(othPerformances, sort);
                
                int countToAdd = total - advPerformances.Count;
                advPerformances.AddRange(othPerformances.Take(countToAdd));
            }

            // Sort and return
            advPerformances = this.Sort(advPerformances, sort);
            return advPerformances;
        }

        /// <summary>
        ///  Zoradí pretekárov
        /// </summary>
        public List<Performance> Sort(List<Performance> performances, PerformancesSortEnum sort)
        {
            switch (sort)
            {
                case PerformancesSortEnum.StartList:
                    performances = performances
                        .OrderBy(p => p.StartNumber)
                        .ThenBy(p => p.Country.Name)
                        .ThenBy(p => p.Surname)
                        .ThenBy(p => p.FirstName)
                        .ToList();
                    break;

                case PerformancesSortEnum.Results:
                    performances = performances
                        .OrderBy(p => p.Code)
                        .ThenBy(p => p.Rank)
                        .ToList();
                    break;

                default:
                    performances = performances
                        .OrderBy(p => p.Country.Name)
                        .ThenBy(p => p.Surname)
                        .ThenBy(p => p.FirstName)
                        .ToList();
                    break;
            }

            // TODO: Improve sorting of performances

            return performances;
        }





    }
}
